package cn.kennylee.codehub.mybatis.das.extension;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.kennylee.codehub.common.das.AbstractDasQueryStrategy;
import cn.kennylee.codehub.common.das.DasQueryStrategy;
import cn.kennylee.codehub.common.das.dto.PageDto;
import cn.kennylee.codehub.mybatis.das.eo.BaseEo;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.github.yulichang.query.MPJQueryWrapper;
import com.github.yulichang.toolkit.JoinWrappers;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;

/**
 * MyBatis查询策略
 * <p>Created on 2024/12/11.</p>
 *
 * @author kennylee
 * @since 0.0.1
 */
@Slf4j
public class MyBatisDasQueryStrategy<P extends Serializable, E extends PageDto, T extends BaseEo<P>> extends AbstractDasQueryStrategy<E, T, AbstractWrapper<T, String, ?>, AbstractWrapper<T, String, ?>> {

    private volatile AbstractWrapper<T, String, ?> wrapper;

    private static final Set<String> IGNORE_PROPS = new HashSet<>();

    static {
        IGNORE_PROPS.addAll(Arrays.stream(ClassUtil.getDeclaredFields(PageDto.class)).filter(it -> !Modifier.isFinal(it.getModifiers())).map(Field::getName).collect(Collectors.toSet()));
        if (log.isDebugEnabled()) {
            log.debug("忽略的搜索条件dto字段名列表: {}", IGNORE_PROPS);
        }
    }

    @Override
    protected AbstractWrapper<T, String, ?> newCriteria(String propName) {
        if (Objects.isNull(wrapper)) {
            Class<T> clazz = getEntityClass();
            MPJQueryWrapper<T> queryWrapper = JoinWrappers.query(clazz).selectAll(clazz);
            wrapper = queryWrapper;
            return queryWrapper;
        }
        return wrapper;
    }

    @Override
    protected AbstractWrapper<T, String, ?> getQuery() {
        return newCriteria(null);
    }

    @Override
    protected void addIsNotNullCriteria(AbstractWrapper<T, String, ?> criteria, String propName) {
        criteria.isNotNull(propName);
    }

    @Override
    protected void addIsNullCriteria(AbstractWrapper<T, String, ?> criteria, String propName) {
        criteria.isNull(propName);
    }

    @Override
    protected void addNotLikeCriteria(AbstractWrapper<T, String, ?> criteria, String propName, Object val) {
        criteria.notLike(propName, val);
    }

    @Override
    protected void addSufLikeCriteria(AbstractWrapper<T, String, ?> criteria, String propName, Object val) {
        criteria.likeRight(propName, val);
    }

    @Override
    protected void addPreLikeCriteria(AbstractWrapper<T, String, ?> criteria, String propName, Object val) {
        criteria.likeLeft(propName, val);
    }

    @Override
    protected void addNotInCriteria(AbstractWrapper<T, String, ?> criteria, String propName, Collection<?> vals) {
        criteria.notIn(propName, vals);
    }

    @Override
    protected void addInCriteria(AbstractWrapper<T, String, ?> criteria, String propName, Collection<?> vals) {
        criteria.in(propName, vals);
    }

    @Override
    protected void addLikeCriteria(AbstractWrapper<T, String, ?> criteria, String propName, Object val) {
        criteria.like(propName, val);
    }

    @Override
    protected void addLeCriteria(AbstractWrapper<T, String, ?> criteria, String propName, Object val) {
        criteria.le(propName, val);
    }

    @Override
    protected void addLtCriteria(AbstractWrapper<T, String, ?> criteria, String propName, Object val) {
        criteria.lt(propName, val);
    }

    @Override
    protected void addGeCriteria(AbstractWrapper<T, String, ?> criteria, String propName, Object val) {
        criteria.ge(propName, val);
    }

    @Override
    protected void addEqCriteria(AbstractWrapper<T, String, ?> criteria, String propName, Object val) {
        criteria.eq(propName, val);
    }

    @Override
    protected void addNeCriteria(AbstractWrapper<T, String, ?> criteria, String propName, Object val) {
        criteria.ne(propName, val);
    }

    @Override
    protected void addGtCriteria(AbstractWrapper<T, String, ?> criteria, String propName, Object val) {
        criteria.gt(propName, val);
    }

    @Override
    protected Set<String> ignoreSearchProps() {
        return Collections.unmodifiableSet(IGNORE_PROPS);
    }

    @Override
    protected String convertDbPropName(String entityPropName) {
        // mysql命名一般是下划线风格
        return StrUtil.toUnderlineCase(entityPropName);
    }

    @Override
    public DasQueryStrategy<E, T> newInstance() {
        return new MyBatisDasQueryStrategy<>();
    }
}
